package com.iussoft.gateway.routes;


import java.net.URI;
import java.util.*;

import com.apache.exception.BusinessException;
import com.apache.tools.DataMap;
import com.apache.tools.StrUtil;
import com.iussoft.manager.RedisManager;
import com.iussoft.tools.JedisPoolFactory;
import com.iussoft.tools.LoggerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;

import com.alibaba.fastjson.JSON;

import reactor.core.publisher.Mono;

/**
 * @description: 核心配置类，项目初始化加载数据库的路由配置
 * @author: Hou Dayu
 * @date: Created in 2020/2/24
 *
 * serviceId:UCT-SERVER
 * uri:转发地址
 * predicates:/uct/*访问路径
 * filters:1过滤
 * order:0须序
 * remarks:备注
 */
@Service
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner {

    @Autowired
    private RedisRouteDefinitionRepository routeDefinitionWriter;
    //自己的获取数据dao
    @Autowired
    private GatewayDefineService gatewayDefineService;

    @Autowired
    @Qualifier("redisManager")
    private RedisManager redisManager;

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    @Override
    public void run(String... args){
        try {
            this.loadRouteConfig();
            loadWhiteList("");
        }catch (Exception e){
            LoggerUtil.error("Gateway configuration information loading error:"+e.getMessage());
        }
    }

    public String loadRouteConfig() throws BusinessException {
        //从数据库拿到启用中的路由配置
        List<DataMap> gatewayRouteList = gatewayDefineService.gatewayDefineList(new HashMap<>());
        LoggerUtil.info("网关配置信息：=====>"+ JSON.toJSONString(gatewayRouteList));
        gatewayRouteList.forEach(gatewayRoute -> {
            RouteDefinition definition = saveDefine(gatewayRoute);
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        });
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }

    //更新路由
    public String update(Map<String,String> param) {
        try {
            routeDefinitionWriter.delete(Mono.just(param.get("infoId"))).subscribe();
        } catch (Exception e) {
            return "update fail,not find route  routeId: "+param.get("infoId");
        }
        try {
            DataMap<String> data = gatewayDefineService.gatewayDefineById(param.get("infoId"));
            RouteDefinition definition = saveDefine(data);
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            redisManager.createObjCache(param.get("infoId"),JSON.toJSONString(data),0,JedisPoolFactory.DATABASE);
            return "success";
        } catch (Exception e) {
            return "update route  fail";
        }
    }

    public String saveRoute(Map<String,String> param){
        try {
            RouteDefinition definition = saveDefine(param);
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception e) {
            return "save route  fail";
        }
    }

    public void deleteRoute(String routeId){
        routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
        redisManager.delObjCache(routeId,JedisPoolFactory.DATABASE);
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    private RouteDefinition saveDefine(Map<String, String> param){
        RouteDefinition definition = new RouteDefinition();
        definition.setId(param.get("infoId"));
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();

        predicate.setName("Path");
        predicateParams.put("pattern",param.get("predicates"));
        predicateParams.put("pathPattern", param.get("predicates"));
        predicateParams.put("sensitiveHeaders","*");
        predicate.setArgs(predicateParams);
        List<FilterDefinition> filters = new ArrayList<>();
        /*FilterDefinition filterDef0 = new FilterDefinition();
        filterDef0.setName("StripPrefix");
        filterDef0.setArgs(new HashMap(){{put("_genkey_0",StrUtil.doNull(param.get("stripPrefix"),"1"));}});
        filters.add(filterDef0);*/
        FilterDefinition filterDef = new FilterDefinition();
        filterDef.setName("RequestRateLimiter");
        Map<String, String> filter2 = new HashMap<>(8);
        String limiterRate = StrUtil.doNull(param.get("limiterRate"), "1000");
        // 令牌桶流速,允许用户每秒处理多少个请求
        filter2.put("redis-rate-limiter.replenishRate", limiterRate);
        //令牌桶容量，允许在一秒钟内完成的最大请求数
        filter2.put("redis-rate-limiter.burstCapacity", StrUtil.doNull(param.get("limiterCapacity"), "5000"));
        // 限流策略(#{@BeanName})
        filter2.put("key-resolver", "#{@apiKeyResolver}");//#{@remoteAddrKeyResolver}/#{@ipKeyResolver}
        //熔断机制 需要引spring-cloud-starter-netflix-hystrix
        filter2.put("name","default");
        filter2.put("fallbackUri","forward:/fallback");//fallback 被调用时，请求将转发到/incaseoffailureuset这个 URI
        filterDef.setArgs(filter2);
        filters.add(filterDef);
        definition.setFilters(filters);
        if(StrUtil.isNotNull(param.get("version"))) {//权重设置
            PredicateDefinition Weight = new PredicateDefinition();
            Map<String, String> filter1Params = new HashMap<>(8);
            //filter1Params.put(param.get("version"), param.get("weight"));
            filter1Params.put("_genkey_0",param.get("version"));
            filter1Params.put("_genkey_1",param.get("weight"));
            Weight.setName("Weight");
            Weight.setArgs(filter1Params);
            definition.setPredicates(Arrays.asList(predicate,Weight));
        } else {
            definition.setPredicates(Arrays.asList(predicate));
        }
        URI uri = null;
        if(StrUtil.isNotNull(param.get("url")) && param.get("url").startsWith("http")){
            //http地址
            uri = UriComponentsBuilder.fromHttpUrl(param.get("url")).build().toUri();
        }else{
            //注册中心
            uri = UriComponentsBuilder.fromUriString("lb://"+param.get("serverId")).build().toUri();
        }
        definition.setUri(uri);
        definition.setOrder(Integer.valueOf(StrUtil.doNull(param.get("routeOrder"),"1")));
        redisManager.createObjCache(param.get("infoId"),JSON.toJSONString(param),0,JedisPoolFactory.DATABASE);
        return definition;
    }

    public void loadWhiteList(String ip){
        if(StrUtil.isNotNull(ip)){
            redisManager.delObjCache(ip,JedisPoolFactory.DATABASE);
        } else {
            Map<String, String> param = new HashMap<>();
            param.put("enabled", "1");
            List<DataMap> whiteList = gatewayDefineService.getWhiteList(param);
            whiteList.forEach(white -> {
                redisManager.createObjCache(white.get("useIp").toString(), JSON.toJSONString(white), 0, JedisPoolFactory.DATABASE);
            });
        }
    }
}
